/*
board-games:	An educational framework for board games programming.
    Copyright (C) 2008  Daniel Gutson, FuDePAN
    
    This file is part of board-games.

    board-games is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    board-games is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
*/

#ifndef REFEREE_SIDE
#define REFEREE_SIDE

#include "referee_sdk/referee.h"
#include "players_side.h"


class RefereeData
{
    ShObj shobj;
    Referee* referee;
public:
    struct SetupException : std::exception
    {
        const std::string msg;
        
        SetupException(const char* msg)
            : msg(msg)
        {}
        
        SetupException(const SetupException& other) : msg(other.msg) {}
        
        ~SetupException() throw() {}
        
        virtual const char* what() const throw()
        {
            return msg.c_str();
        }
    };
    
    RefereeData() : referee(NULL) {}
    ~RefereeData()
    {
        delete referee;
    }
    
    void load(const char* objname, PlayersProxy* playersProxy);
    int do_match(void) throw(PlayerID, const char*);
};

class PlayersProxyImpl : public PlayersProxy
{
    PlayerData* const playersData;
    const size_t      players;
    size_t            boards;
    
    virtual size_t number_of_players() const { return players; }
    
    virtual void send_message(PlayerID playerID, const void* msgdata, size_t size) throw(PlayerID)
    {
        try
        {
            playersData[playerID].send_message(msgdata, size);
        }
        catch(...)
        {
            throw playerID;
        }
    }
    
    virtual void receive_message(PlayerID playerID, void*& response, size_t& response_size) throw(PlayerID)
    {
        try
        {
            playersData[playerID].receive_message(response, response_size);
        }
        catch(...)
        {
            throw playerID;
        }
    }
    
    virtual bool player_running(PlayerID playerID) const
    {
        return playersData[playerID].is_running();
    }

    virtual bool player_running(_ALL_PLAYERS) const
    {
        bool all_running;
        PlayerID p = 0;
        do
        {
            all_running = player_running(p++);
        }
        while(all_running && p<players);
        
        return all_running;
    }
    
    void kill_player(PlayerID playerID)
    {
        playersData[playerID].kill();
    }
    

    virtual void set_player_timeout(PlayerID playerID, size_t milliseconds)
    {
        playersData[playerID].set_player_timeout(milliseconds);
    }
    
    virtual void set_player_timeout(PlayerID playerID, _NEVER_TIMEOUT)
    {
        playersData[playerID].disable_timeout();
    }
        
    virtual void set_player_timeout(_ALL_PLAYERS, size_t milliseconds)
    {
        for (PlayerID i=0; i<players; i++)
            set_player_timeout(i, milliseconds);
    }
    
    virtual void set_player_timeout(_ALL_PLAYERS, _NEVER_TIMEOUT)
    {
        for (PlayerID i=0; i<players; i++)
            set_player_timeout(i, NEVER);
    }

    virtual void send_board(PlayerID playerID, SharedBoard& board)
    {
        boards++;
        const int shmid = board.share_create(boards);
        PlayersProxy::send_message(playerID, shmid);
    }

    virtual void send_board(_ALL_PLAYERS, SharedBoard& board)
    {
        for (PlayerID i=0; i<players; i++)
            send_board(i, board);
    }

public:
    PlayersProxyImpl(size_t number_of_players)
        : playersData(new PlayerData[number_of_players]), players(number_of_players), boards(0)
    {}
    
    ~PlayersProxyImpl()
    {            
        //kill all:
        for(PlayerID i=0; i<players; ++i)
        {
            if(player_running(i))
                kill_player(i);
        }
        
        delete [] playersData;
        
    }

    void load(PlayerID player, const char* objname)
    {
        playersData[player].load(objname);
    }
};

#endif
